package com.dong.rwmysql.aspect;

import com.dong.rwmysql.annotation.Master;
import com.dong.rwmysql.config.DBContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 默认情况下，所有的查询都走从库，插入/修改/删除走主库。我们通过方法名来区分操作类型（CRUD）
 * <p>
 * 切面不能建立在DAO层，事务是在service开启的，到dao层再切换数据源，那事务就废了
 */
@Aspect
@Component
@Slf4j
public class DataSourceAop {

    @Pointcut("!@annotation(com.dong.rwmysql.annotation.Master) " +
            "&& (execution(* com.dong.rwmysql.service.*.select*(..)) || execution(* com.dong.rwmysql.service..*.find*(..))  ) ")
    public void readPointcut() {

    }

    @Pointcut("@annotation(com.dong.rwmysql.annotation.Master) " +
            "|| execution(* com.dong.rwmysql.service..*.save*(..)) " +
            "|| execution(* com.dong.rwmysql.service..*.add*(..)) " +
            "|| execution(* com.dong.rwmysql.service..*.insert*(..)) " +
            "|| execution(* com.dong.rwmysql.service..*.update*(..)) " +
            "|| execution(* com.dong.rwmysql.service..*.edit*(..)) " +
            "|| execution(* com.dong.rwmysql..*.delete*(..)) " +
            "|| execution(* com.dong.rwmysql..*.remove*(..))")
    public void writePointcut() {

    }

    @Before("readPointcut()")
    public void read(JoinPoint jp) {
        /**
         * JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
         * 常用api:
         *
         * 方法名	功能
         * Signature getSignature();	获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
         * Object[] getArgs();	获取传入目标方法的参数对象
         * Object getTarget();	获取被代理的对象
         * Object getThis();	获取代理对象
         */
        //获取当前的方法信息
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();//方法头指定修饰符(例如static)、返回值类型、方法名、和形式参数。
        Method method = methodSignature.getMethod();

        //判断方法上是否存在注解@Master
        boolean present = method.isAnnotationPresent(Master.class);//判断注解是否存在该元素上，如果有则返回true，否则false
        if (!present) {
            //如果不存在，默认走从库读
            log.info("读取数据从从库");
            DBContextHolder.slave();
        } else {
            //如果存在，走主库读
            log.info("读取数据从主库");
            DBContextHolder.master();
        }
    }

    @Before("writePointcut()")
    public void write() {
        log.info("写数据到主库");
        DBContextHolder.master();
    }

}
